package gshttp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"gitee.com/Sxiaobai/gs/gstool"
	"github.com/spf13/cast"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"time"
)

const (
	ContentTypeGet = iota
	ContentTypePostMultiForm
	ContentTypePostForm
	ContentTypePostJson
)

type Client struct {
	sourceUrl       string
	urlValues       *url.Values
	contentType     int
	headerMap       map[string]string
	cookieList      []*http.Cookie
	body            *bytes.Buffer
	response        *http.Response
	streamBytesNum  int                 //流式输出字节数 如果为0 那么按照streamBytesEnd进行分割
	streamBytesFunc func(string, error) //流式输出回调 数据接收回调
	streamBytesEnd  []byte              //读取到哪个字节后返回 如果为空 那么按照字节数进行接收
	streamResFunc   func([]byte) []byte //流式输出结果通过回调解析后作为真实结果存储到responseByte
	responseByte    []byte
	err             error
	mw              *multipart.Writer
}

func Get(url string) *Client {
	return &Client{
		sourceUrl:   url,
		contentType: ContentTypeGet,
		cookieList:  []*http.Cookie{},
		headerMap:   make(map[string]string),
	}
}

func PostForm(sourceUrl string) *Client {
	headers := make(map[string]string)
	headers["Content-Type"] = "application/x-www-form-urlencoded"
	return &Client{
		sourceUrl:   sourceUrl,
		contentType: ContentTypePostMultiForm,
		headerMap:   headers,
		body:        &bytes.Buffer{},
		cookieList:  []*http.Cookie{},
	}
}

func PostJson(url string) *Client {
	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"
	return &Client{
		sourceUrl:   url,
		contentType: ContentTypePostJson,
		headerMap:   headers,
		body:        &bytes.Buffer{},
		cookieList:  []*http.Cookie{},
	}
}

func QuickGet(url string, timeoutSecond int) ([]byte, error) {
	return Get(url).Request(timeoutSecond).Result()
}

func QuickPostJson(url, body string, timeoutSecond int) ([]byte, error) {
	return PostJson(url).BodyStr(body).Request(timeoutSecond).Result()
}

func QuickPostBuffer(url string, buffer *bytes.Buffer, timeoutSecond int) ([]byte, error) {
	return PostMultiForm(url).BodyBuffer(buffer).Request(timeoutSecond).Result()
}

func PostMultiForm(url string) *Client {
	client := &Client{
		sourceUrl:   url,
		contentType: ContentTypePostMultiForm,
		body:        &bytes.Buffer{},
		cookieList:  []*http.Cookie{},
	}
	client.mw = multipart.NewWriter(client.body)
	headers := make(map[string]string)
	headers["Content-Type"] = client.mw.FormDataContentType()
	client.headerMap = headers
	return client
}

func (h *Client) OpenStreamBytes(streamBytes int, streamBytesFunc func(string, error), streamResFunc func([]byte) []byte) *Client {
	h.streamBytesNum = streamBytes
	if h.streamBytesNum == 0 {
		h.streamBytesNum = 1024
	}
	h.streamBytesFunc = streamBytesFunc
	h.streamResFunc = streamResFunc
	return h
}

func (h *Client) OpenStreamBytesEnd(streamBytesEnd []byte, streamBytesFunc func(string, error), streamResFunc func([]byte) []byte) *Client {
	h.streamBytesEnd = streamBytesEnd
	h.streamBytesFunc = streamBytesFunc
	h.streamResFunc = streamResFunc
	return h
}

func (h *Client) BodyMap(pM map[string]any) *Client {
	if h.err != nil {
		return h
	}
	switch h.contentType {
	case ContentTypePostJson:
		_, copyErr := io.Copy(h.body, bytes.NewReader([]byte(gstool.JsonEncode(pM))))
		h.err = copyErr
	case ContentTypePostForm:
		for k, v := range pM {
			h.urlValues.Add(k, cast.ToString(v))
		}
		_, encodeErr := io.WriteString(h.body, h.urlValues.Encode())
		if encodeErr != nil {
			h.err = encodeErr
			return h
		}
	case ContentTypeGet:
		for k, v := range pM {
			h.urlValues.Add(k, cast.ToString(v))
		}
		h.sourceUrl = h.sourceUrl + `?` + h.urlValues.Encode()
	case ContentTypePostMultiForm:
		for k, v := range pM {
			tw, twErr := h.mw.CreateFormField(k)
			if twErr != nil {
				h.err = twErr
				return h
			}
			_, writeErr := tw.Write([]byte(cast.ToString(v)))
			if writeErr != nil {
				h.err = writeErr
				return h
			}
		}
	default:
		h.err = errors.New(`未知的类型`)
	}
	return h
}

func (h *Client) BodyStr(body string) *Client {
	if h.err != nil {
		return h
	}
	switch h.contentType {
	case ContentTypePostJson, ContentTypePostForm, ContentTypePostMultiForm:
		_, copyErr := io.Copy(h.body, bytes.NewReader([]byte(body)))
		h.err = copyErr
	default:
		h.err = errors.New(`不支持向get请求写入body`)
	}
	return h
}

func (h *Client) BodyBuffer(body *bytes.Buffer) *Client {
	if h.err != nil {
		return h
	}
	switch h.contentType {
	case ContentTypePostJson, ContentTypePostForm, ContentTypePostMultiForm:
		h.body = body
	default:
		h.err = errors.New(`不支持向get请求写入body`)
	}
	return h
}

func (h *Client) Headers(m map[string]string) *Client {
	h.headerMap = m
	return h
}

func (h *Client) Cookies(m []*http.Cookie) *Client {
	h.cookieList = m
	return h
}

func (h *Client) BodyFile(formKey, filePath, fileName string) *Client {
	if h.contentType != ContentTypePostMultiForm {
		h.err = errors.New(`上传文件仅支持PostMultiForm`)
		return h
	}
	file, openErr := os.Open(filePath)
	if openErr != nil {
		h.err = openErr
		return h
	}
	defer func(file *os.File) {
		closeErr := file.Close()
		if closeErr != nil {
			//TODO
		}
	}(file)
	if fileName == `` {
		fileName = file.Name()
	}
	fw, fwErr := h.mw.CreateFormFile(formKey, fileName)
	if fwErr != nil {
		h.err = fwErr
		return h
	}
	_, copyErr := io.Copy(fw, file)
	if copyErr != nil {
		h.err = copyErr
		return h
	}
	return h
}

func (h *Client) Request(timeoutSecond int) *Client {
	if h.err != nil {
		return h
	}
	var req *http.Request
	var reqErr error
	if h.contentType == ContentTypePostMultiForm && h.mw != nil {
		if err := h.mw.Close(); err != nil {
			h.err = err
			return h
		}
	}
	switch h.contentType {
	case ContentTypePostJson, ContentTypePostForm, ContentTypePostMultiForm:
		req, reqErr = http.NewRequest("POST", h.sourceUrl, h.body)
	case ContentTypeGet:
		req, reqErr = http.NewRequest("GET", h.sourceUrl, nil)
	default:
		h.err = errors.New(`未知的类型`)
		return h
	}
	if reqErr != nil {
		h.err = reqErr
		return h
	}
	for k, v := range h.headerMap {
		req.Header.Add(k, v)
	}
	for _, v := range h.cookieList {
		req.AddCookie(v)
	}
	client := &http.Client{
		Timeout: time.Duration(timeoutSecond) * time.Second,
		Transport: &http.Transport{
			DisableKeepAlives: true, //禁用保持长连接
		},
	}
	var responseErr error
	h.response, responseErr = client.Do(req)
	if responseErr != nil {
		h.err = responseErr
		return h
	}
	// 检查响应状态码
	if h.response.StatusCode != http.StatusOK {
		h.err = errors.New(fmt.Sprintf(`http status code error ：%d`, h.response.StatusCode))
		return h
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			//TODO
		}
	}(h.response.Body)
	if h.streamResFunc != nil { //流式输出处理
		if h.streamBytesNum > 0 { //按照固定字节读取
			reader := io.Reader(h.response.Body)
			buffer := make([]byte, h.streamBytesNum)
			for {
				n, err := reader.Read(buffer)
				if err == io.EOF {
					break
				}
				if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
					h.streamBytesFunc(``, errors.New(fmt.Sprintf("Error reading response: %v", err)))
					break
				}
				h.streamBytesFunc(string(buffer[:n]), nil)
				if h.streamResFunc != nil {
					h.responseByte = append(h.responseByte, h.streamResFunc(buffer[:n])...)
				} else {
					h.responseByte = append(h.responseByte, buffer[:n]...)
				}
			}
		} else { //按照字符串结尾读取
			reader := bufio.NewScanner(h.response.Body)
			reader.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
				if atEOF && len(data) == 0 {
					return 0, nil, nil
				}
				if i := bytes.Index(data, h.streamBytesEnd); i >= 0 {
					return i + 2, data[0:i], nil
				}
				if atEOF {
					return len(data), data, nil
				}
				return 0, nil, nil
			})

			for reader.Scan() {
				resBytes := reader.Bytes()
				h.streamBytesFunc(string(resBytes), nil)
				h.responseByte = append(h.responseByte, h.streamResFunc(resBytes)...)
			}
			//按照单个字节截取
			//for {
			//	resBytes, err := reader.ReadBytes(h.streamBytesEnd)
			//	if err == io.EOF {
			//		break
			//	}
			//	if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
			//		h.streamBytesFunc(``, errors.New(fmt.Sprintf("Error reading response: %v", err)))
			//		break
			//	}
			//	h.streamBytesFunc(string(resBytes), nil)
			//	if h.streamResFunc != nil {
			//		h.responseByte = append(h.responseByte, h.streamResFunc(resBytes)...)
			//	} else {
			//		h.responseByte = append(h.responseByte, resBytes...)
			//	}
			//}
		}
	} else {
		responseData, responseDataErr := io.ReadAll(h.response.Body)
		if responseDataErr != nil {
			h.err = responseDataErr
			return h
		}
		h.responseByte = responseData
	}

	return h
}

func (h *Client) ResponseHeader() http.Header {
	if h.response == nil {
		return http.Header{}
	}
	return h.response.Header
}

func (h *Client) Response() *http.Response {
	return h.response
}

func (h *Client) Result() ([]byte, error) {
	return h.responseByte, h.err
}

func (h *Client) ResultStr() (string, error) {
	return cast.ToString(h.responseByte), h.err
}

func (h *Client) ErrInfo() error {
	return h.err
}

func (h *Client) JsonDecode(a any) error {
	if h.err != nil {
		return h.err
	}
	decodeErr := gstool.JsonDecode(cast.ToString(h.responseByte), a)
	if decodeErr != nil {
		return decodeErr
	}
	return nil
}
